﻿#include "WTLLibrary.h"
#include "MLibraryV2.h"

using namespace ManagedSquish;

namespace LibraryEditor
{

    bool WTLLibrary::isNewVersion() const
    {
        return IsNewVersion;
    }

    void WTLLibrary::setNewVersion(bool value)
    {
        IsNewVersion = value;
    }

    WTLLibrary::WTLLibrary(const std::string &filename) : _fileName(Path::ChangeExtension(filename, ""))
    {
        Initialize();
    }

    void WTLLibrary::Initialize()
    {
        _initialized = true;
        if (!FileSystem::fileExists(_fileName + ".wtl"))
        {
            return;
        }
        _fStream = new FileStream(_fileName + ".wtl", FileMode::Open, FileAccess::ReadWrite);
        _bReader = new BinaryReader(_fStream);
        LoadImageInfo();

        for (int i = 0; i < _count; i++)
        {
            CheckMImage(i);
        }
    }

    void WTLLibrary::LoadImageInfo()
    {
        _fStream->Seek(2, SeekOrigin::Begin);
        auto version = StringHelper::trimEnd(System::Text::Encoding::UTF8->GetString(_bReader->ReadBytes(20)), "\0");
        setNewVersion(version == "ILIB v2.0-WEMADE");

        _fStream->Seek(28, SeekOrigin::Begin);
        _count = _bReader->ReadInt32();
        _indexList = std::vector<int>(_count);
        Images = std::vector<WTLImage*>(_count);

        if (isNewVersion())
        {
            _bReader->BaseStream->Seek(_fStream->Length - _count * 4, SeekOrigin::Begin);
        }

        for (int i = 0; i < _count; i++)
        {
            _indexList[i] = _bReader->ReadInt32();
        }
    }

    void WTLLibrary::Close()
    {
        if (_fStream != nullptr)
        {
            _fStream->Dispose();
        }
        if (_bReader != nullptr)
        {
            _bReader->Dispose();
        }
    }

    void WTLLibrary::CheckMImage(int index)
    {
        if (!_initialized)
        {
            Initialize();
        }

        if (index < 0 || index >= Images.size())
        {
            return;
        }
        if (_indexList[index] == 0)
        {
            Images[index] = new WTLImage(index, isNewVersion());
            return;
        }
        WTLImage *image = Images[index];

        if (image == nullptr)
        {
            _fStream->Seek(_indexList[index], SeekOrigin::Begin);
            image = new WTLImage(index, isNewVersion(), _bReader);
            Images[index] = image;
            image->CreateTexture(_bReader);
        }
    }

    void WTLLibrary::ToMLibrary()
    {
        std::string fileName = Path::ChangeExtension(_fileName, ".Lib");

        if (FileSystem::fileExists(fileName))
        {
            File::Delete(fileName);
        }

        MLibraryV2 *library = new MLibraryV2(fileName);
        library->Images = std::vector<MLibraryV2::MImage*>();
        library->IndexList = std::vector<int>();
        library->Count = Images.size();
        //library.Save();

        for (int i = 0; i < library->Count; i++)
        {
            library->Images.push_back(nullptr);
        }

        ParallelOptions *options = new ParallelOptions();
        options->MaxDegreeOfParallelism = 8;
        try
        {
            Parallel::For(0, Images.size(), options, [&] (std::any i)
            {
                WTLImage *image = Images[i];
                if (image->HasMask)
                {
                    MLibraryV2::MImage *tempVar = new MLibraryV2::MImage(image->Image, image->MaskImage);
                    tempVar->X = image->X;
                    tempVar->Y = image->Y;
                    tempVar->ShadowX = image->ShadowX;
                    tempVar->ShadowY = image->ShadowY;
                    tempVar->Shadow = image->Shadow;
                    tempVar->MaskX = image->MaskX;
                    tempVar->MaskY = image->MaskY;
                    library->Images[i] = tempVar;

                delete tempVar;
                }
                else
                {
                    MLibraryV2::MImage *tempVar2 = new MLibraryV2::MImage(image->Image);
                    tempVar2->X = image->X;
                    tempVar2->Y = image->Y;
                    tempVar2->ShadowX = image->ShadowX;
                    tempVar2->ShadowY = image->ShadowY;
                    tempVar2->Shadow = image->Shadow;
                    library->Images[i] = tempVar2;

                delete tempVar2;
                }
            });
        }
        catch (const std::runtime_error &e1)
        {
//C# TO C++ CONVERTER TODO TASK: A 'delete options' statement was not added since options was passed to a method or constructor. Handle memory management manually.
            delete library;
            throw;
        }
//C# TO C++ CONVERTER TODO TASK: There is no C++ equivalent to the exception 'finally' clause:
        finally
        {
            library->Save();
        }

        // Operation finished.
        // System.Windows.Forms.MessageBox.Show("Converted " + fileName + " successfully.",
        //    "Shanda Information",
        //        System.Windows.Forms.MessageBoxButtons.OK,
        //            System.Windows.Forms.MessageBoxIcon.Information,
        //                System.Windows.Forms.MessageBoxDefaultButton.Button1);

//C# TO C++ CONVERTER TODO TASK: A 'delete options' statement was not added since options was passed to a method or constructor. Handle memory management manually.
        delete library;
    }

    int WTLImage::getDataOffset() const
    {
        return DataOffset;
    }

    int WTLImage::getIndex() const
    {
        return Index;
    }

    bool WTLImage::isNewVersion() const
    {
        return IsNewVersion;
    }

    unsigned char WTLImage::getImageTextureType() const
    {
        return ImageTextureType;
    }

    unsigned char WTLImage::getMaskTextureType() const
    {
        return MaskTextureType;
    }

    WTLImage::WTLImage(int index, bool isNewVersion)
    {
        Index = index;
        IsNewVersion = isNewVersion;
        _fBytes = std::vector<unsigned char>(0);
        _MaskfBytes = std::vector<unsigned char>(0);
    }

    WTLImage::WTLImage(int index, bool isNewVersion, BinaryReader *bReader) : Width(bReader->ReadInt16()), Height(bReader->ReadInt16()), X(bReader->ReadInt16()), Y(bReader->ReadInt16()), ShadowX(bReader->ReadInt16()), ShadowY(bReader->ReadInt16())
    {
        Index = index;
        IsNewVersion = isNewVersion;

        if (isNewVersion())
        {
            auto imageU1 = bReader->ReadByte();
            ImageTextureType = bReader->ReadByte();
            auto maskU1 = bReader->ReadByte();
            MaskTextureType = bReader->ReadByte();

            HasMask = getMaskTextureType() > 0;
            Length = bReader->ReadInt32();
            if (Length % 4 > 0)
            {
                Length += 4 - (Length % 4);
            }
            DataOffset = static_cast<int>(bReader->BaseStream->Position);
        }
        else
        {
            Length = bReader->ReadByte() | bReader->ReadByte() << 8 | bReader->ReadByte() << 16;
            Shadow = bReader->ReadByte();
            HasMask = ((Shadow >> 7) == 1) ? true : false;
        }
    }

    void WTLImage::DecompressBlock(std::vector<unsigned char> &newPixels, std::vector<unsigned char> &block)
    {
        std::vector<unsigned char> colours(8);
        std::copy_n(block.begin(), 8, colours.begin());

        std::vector<unsigned char> codes(16);

        int a = Unpack(block, 0, codes, 0);
        int b = Unpack(block, 2, codes, 4);

        for (int i = 0; i < 3; i++)
        {
            int c = codes[i];
            int d = codes[4 + i];

            if (a <= b)
            {
                codes[8 + i] = static_cast<unsigned char>((c + d) / 2);
                codes[12 + i] = 0;
            }
            else
            {
                codes[8 + i] = static_cast<unsigned char>((2 * c + d) / 3);
                codes[12 + i] = static_cast<unsigned char>((c + 2 * d) / 3);
            }
        }

        codes[8 + 3] = 255;
        codes[12 + 3] = (a <= b) ? static_cast<unsigned char>(0) : static_cast<unsigned char>(255);
        for (int i = 0; i < 4; i++)
        {
            if ((codes[i * 4] == 0) && (codes[(i * 4) + 1] == 0) && (codes[(i * 4) + 2] == 0) && (codes[(i * 4) + 3] == 255))
            { //dont ever use pure black cause that gives transparency issues
                codes[i * 4] = 1;
                codes[(i * 4) + 1] = 1;
                codes[(i * 4) + 2] = 1;
            }
        }

        std::vector<unsigned char> indices(16);
        for (int i = 0; i < 4; i++)
        {
            unsigned char packed = block[4 + i];

            indices[0 + i * 4] = static_cast<unsigned char>(packed & 0x3);
            indices[1 + i * 4] = static_cast<unsigned char>((packed >> 2) & 0x3);
            indices[2 + i * 4] = static_cast<unsigned char>((packed >> 4) & 0x3);
            indices[3 + i * 4] = static_cast<unsigned char>((packed >> 6) & 0x3);
        }

        for (int i = 0; i < 16; i++)
        {
            unsigned char offset = static_cast<unsigned char>(4 * indices[i]);
            for (int j = 0; j < 4; j++)
            {
                newPixels[4 * i + j] = codes[offset + j];
            }
        }
    }

    int WTLImage::Unpack(std::vector<unsigned char> &packed, int srcOffset, std::vector<unsigned char> &colour, int dstOffSet)
    {
        int value = packed[0 + srcOffset] | (packed[1 + srcOffset] << 8);
        // get components in the stored range
        unsigned char red = static_cast<unsigned char>((value >> 11) & 0x1F);
        unsigned char green = static_cast<unsigned char>((value >> 5) & 0x3F);
        unsigned char blue = static_cast<unsigned char>(value & 0x1F);

        // Scale up to 24 Bit
        colour[2 + dstOffSet] = static_cast<unsigned char>((red << 3) | (red >> 2));
        colour[1 + dstOffSet] = static_cast<unsigned char>((green << 2) | (green >> 4));
        colour[0 + dstOffSet] = static_cast<unsigned char>((blue << 3) | (blue >> 2));
        colour[3 + dstOffSet] = 255;
        //*/
        return value;
    }
}
